package com.dirven.game.geometry;

import com.dirven.game.Coord;

public class Line {
	
	// Coordinates
	float x1,	
		  y1,
		  x2,
		  y2;
	
	// Restrictions
	public float minX, maxX,
		  		 minY, maxY;
	
	float m = 0;	// Slope
	float b = 0;	// y-intercept
	
	public boolean isVertical = false,
				   isHorizontal = false;
	float x = 0; // Only used for vertical lines.
	
	public Line(float x1, float y1, float x2, float y2) {
		
		setPoints(x1, y1, x2, y2);
	}
	
	public void setPoints(float x1, float y1, float x2, float y2) {
		this.x1 = x1;
		this.y1 = y1;
		this.x2 = x2;
		this.y2 = y2;
		
		isVertical = isHorizontal = false;
		
		if( (x2-x1) != 0) {
			m = (y2 - y1) / (x2 - x1);
			isVertical = false;
		} else {
			// vertical line
			isVertical = true;
			x = x1 = x2;
		}
		
		if(m == 0 && isVertical) {
			isHorizontal = false;
		} else if(m == 0 && !isVertical) {
			isHorizontal = true;
		} else if(m != 0) {
			isHorizontal = isVertical = false;
		}
		
		b = y1 - m * x1;
		//or b = y2 - m * x2;
		
		
		// Get restrictions
		if(x1 > x2) {
			maxX = x1;
			minX = x2;
		} else if(x1 < x2) {
			maxX = x2;
			minX = x1;
		} else if(x1 == x2) {
			minX = maxX = x1;
		}
		
		if(y1 > y2) {
			maxY = y1;
			minY = y2;
		} else if(y1 < y2) {
			maxY = y2;
			minY = y1;
		} else if(y1 == y2) {
			minY = maxY = y1;
		}
	}
	
	public Line (Coord c1, Coord c2) {
		
	}
	
	public float getY(float x) /*throws Exception*/ {
		if(!this.isVertical()) {
			return this.m*x + this.b;
		} else {
			return Float.POSITIVE_INFINITY;
		}
	}
	
	public float getX(float y) /*throws Exception*/ {
		if(!this.isHorizontal() && !this.isVertical()) {
			return (y - this.b) / this.m;
		} else if(!this.isHorizontal() && this.isVertical()) {
			return x1;
		} else if(this.isHorizontal() && !this.isVertical()) {
			return Float.POSITIVE_INFINITY;
		}
		return Float.POSITIVE_INFINITY;
	}
	
	public Coord getPOI(Line line2) {
		// x = (b2 - b1) / (m1 - m2)
		float xPOI,
			  yPOI;
		
		// Two vertical lines don't intersect.
		if(isVertical() && line2.isVertical()) {
			return null;
		}
		// Two horizontal lines don't intersect.
		if(isHorizontal() && line2.isHorizontal()) {
			return null;
		}
		
		if(isVertical() == false) {
			xPOI = (line2.b - this.b) / (this.m - line2.m);
		} else {
			xPOI = x;
		}
		
		yPOI = this.getY(xPOI);
		
		return new Coord(xPOI, yPOI);
	}
	
	public boolean isVertical() {
		return isVertical;
	}
	
	public boolean isHorizontal() {
		return isHorizontal;
	}
	
	/**
	 * Get the angle at which this line intersects the given line. The angle is relative to this line.
	 * @param line
	 * @return
	 */
	public float getIntersectingAngle(Line line) {
		if(getPOI(line) == null) {
			return Float.NaN;
		}
		
		return (float)Math.toDegrees(Math.atan( Math.abs((this.m - line.m )/(1 + this.m*line.m))));
	}
	
	public boolean intersects(Line line2) {
		
		Coord POI = getPOI(line2);
		
		if(POI.x >= minX && POI.x <= maxX && POI.y >= minY && POI.y <= maxY) {
			
			POI = null;
			
			return true;
		} else {
			
			POI = null;
			
			return false;
		}
		
	}
	
	public boolean intersects(Rectangle rect) {
		return rect.intersectsLine(this);
	}
}
